#include "WebSocketHandler.h"
#include <curl/curl.h>
#include <json/json.h>
#include <hv/WebSocketClient.h>
#include <atomic>
#include "config/Config.h"
#include "algoHandler/AlgoHandler.h"

#define CAMERA_PASSWARD "Maxvision2091"

class WebSocketHandler {
public:
    static WebSocketHandler* getInstance();
    ~WebSocketHandler();
public:
    CURL* mCurl;
    hv::WebSocketClient mClient;
    std::atomic_bool isInited;
    std::string mAddress;
private:
    WebSocketHandler();
};

static size_t on_write_data(void* buffer, size_t size, size_t nmemb, void* lpVoid) {
    std::string* str = dynamic_cast<std::string*>((std::string*)lpVoid);
    if (nullptr == str || nullptr == buffer) {
        return -1;
    }
    char* data = reinterpret_cast<char*>(buffer);
    str->append(data, size * nmemb);
    return nmemb;
}

WebSocketHandler::WebSocketHandler() {
    isInited.store(false);
}

WebSocketHandler::~WebSocketHandler() {

}

WebSocketHandler* WebSocketHandler::getInstance() {
    static WebSocketHandler handler;
    return &handler;
}

void reInitWebSocketHandler() {
    deInitWebSocketHandler();
    initWebSocketHandler(WebSocketHandler::getInstance()->mAddress);
}


int initWebSocketHandler(const std::string& address) {
    if(WebSocketHandler::getInstance()->isInited.load()) {
        return 0;
    }
    if(address.find("http") == std::string::npos) return -1;
    WebSocketHandler::getInstance()->mAddress = address;
    WebSocketHandler::getInstance()->mCurl = curl_easy_init();
    if(nullptr == WebSocketHandler::getInstance()->mCurl) return -2;
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_URL, address.c_str());
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_POST, 1);
    Json::Value root;
    root["user"] = "user";
    root["passwd"] = CAMERA_PASSWARD;
    root["type"] = Config::getInstance()->enablePassportSideReg ? 4 : 5;
    Json::FastWriter fw;
    std::string bodyString = fw.write(root);
    fprintf(stderr, "bodyString is %s\n", bodyString.c_str());

    curl_slist *plist = curl_slist_append(NULL, "Content-Type:application/json;charset=UTF-8");
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_HTTPHEADER, plist);
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_POSTFIELDSIZE, strlen(bodyString.c_str()));
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_POSTFIELDS, bodyString.c_str());
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_READFUNCTION, nullptr);
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_WRITEFUNCTION, on_write_data);

    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_CONNECTTIMEOUT_MS, 5000);
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_TIMEOUT, 5000);
    std::string str_response;
    curl_easy_setopt(WebSocketHandler::getInstance()->mCurl, CURLOPT_WRITEDATA, reinterpret_cast<void*>(&str_response));

    CURLcode res = curl_easy_perform(WebSocketHandler::getInstance()->mCurl);
    if(CURLcode::CURLE_OK != res) {
        // TODO
        fprintf(stderr, "code != CURLcode::CURLE_OK when browse %s, code is %d, context is %s\n", address.c_str(), res, str_response.c_str());
        curl_slist_free_all(plist);
        curl_easy_cleanup(WebSocketHandler::getInstance()->mCurl);
        return -1;
    }else {
        // 解析返回的ws地址和token
        Json::Value recvJson;
        Json::Reader reader;
        if (!reader.parse(str_response, recvJson) || !recvJson.isMember("wsAddress") || !recvJson.isMember("token")) {
            fprintf(stderr, "reader.parse error when browse %s, response context is %s\n", address.c_str(), str_response.c_str());
            curl_slist_free_all(plist);
            curl_easy_cleanup(WebSocketHandler::getInstance()->mCurl);
            return -2;
        }
        std::string wsAddress = recvJson["wsAddress"].asString();
        std::string token = recvJson["token"].asString();
        WebSocketHandler::getInstance()->mClient.onopen = []() {

        };
        WebSocketHandler::getInstance()->mClient.onclose = []() {

        };
        WebSocketHandler::getInstance()->mClient.onmessage = [](const std::string& msg) {
            if("startReg" == msg) {
                AlgoHandler::getInstance()->startReg();
            }else if("request" == msg) {
                AlgoHandler::getInstance()->request();
            }else if("stopReg" == msg) {
                AlgoHandler::getInstance()->stopReg();
            }
            if("token is wrong or expired" == msg) {
                std::thread(reInitWebSocketHandler).detach();
            }
        };
        reconn_setting_t reconn;
        reconn.min_delay = 1000;
        reconn.max_delay = 10000;
        reconn.delay_policy = 2;
        WebSocketHandler::getInstance()->mClient.setReconnect(&reconn);

        http_headers headers;
        headers["token"] = token;
        int rc = WebSocketHandler::getInstance()->mClient.open(wsAddress.c_str(), headers);
        if(rc != 0) {
            fprintf(stderr, "websocket open fail. errno is %d, errmsg is %s\n", errno, strerror(errno));
            return -3;
        }
    }
    curl_slist_free_all(plist);
    curl_easy_cleanup(WebSocketHandler::getInstance()->mCurl);
    WebSocketHandler::getInstance()->isInited.store(true);
    return 0;
}

int deInitWebSocketHandler() {
    if(!WebSocketHandler::getInstance()->isInited.load()) return 0;
    WebSocketHandler::getInstance()->mClient.close();
    WebSocketHandler::getInstance()->isInited.store(false);
    return 0;
}

int websocketSendMsg(const std::string& msg) {
    if(!WebSocketHandler::getInstance()->isInited.load()) return -1;
    return WebSocketHandler::getInstance()->mClient.send(msg);
}